package com.bestv.lucene;

import com.bestv.database.vo.ItemRelation;
import org.apache.commons.lang.math.IntRange;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.cjk.CJKAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 节目搜索器
 * User: Henry
 * Date: 14-5-6
 */
public class ProgramSearcher {
    private Logger logger = LoggerFactory.getLogger(getClass());

    private IndexSearcher indexSearcher;

    public ProgramSearcher() {
        indexSearcher = ProgramSearcherManager.getInstance().acquire();
    }

    public void close() {
        ProgramSearcherManager.getInstance().release(indexSearcher);
    }

    public double similarity(Long item, String tags) {
        Analyzer analyzer = new CJKAnalyzer();
        QueryParser tagsParser = new QueryParser("Tags", analyzer);
        Query tagsQuery = null;
        try {
            tagsQuery = tagsParser.parse(tags);
        } catch (ParseException e) {
            logger.error("Error on parse: {}", tags, e);
        }

        Query queryItem = NumericRangeQuery.newLongRange("Id", item, item, true, true);

        BooleanQuery query = new BooleanQuery();
        query.add(queryItem, BooleanClause.Occur.MUST);
        query.add(tagsQuery, BooleanClause.Occur.MUST);

        double sim = 0;
        if (indexSearcher == null) return 1;
        try {
            TopDocs hits = indexSearcher.search(query, 1);
            if (hits.scoreDocs.length > 0)
                sim = hits.scoreDocs[0].score;
        } catch (IOException e) {
            logger.error("Error on search: {}", tags, e);
        }
        return sim;
    }

    public double similarity(Long item, String name, String genre) {
        Analyzer analyzer = new CJKAnalyzer();
        QueryParser nameParser = new QueryParser("Name", analyzer);
        QueryParser genreParser = new QueryParser("Genre", analyzer);
        Query nameQuery = null;
        Query genreQuery = null;
        try {
            nameQuery = nameParser.parse(stem(name));
            genreQuery = genreParser.parse(genre);
        } catch (ParseException e) {
            logger.error("Error on parse: {};{}", name, genre, e);
        }
        if (nameQuery == null || genreQuery == null) return 1;
        genreQuery.setBoost(2.0f);

        Query queryItem = NumericRangeQuery.newLongRange("Id", item, item, true, true);

        BooleanQuery query = new BooleanQuery();
        query.add(queryItem, BooleanClause.Occur.MUST);
        query.add(nameQuery, BooleanClause.Occur.SHOULD);
        query.add(genreQuery, BooleanClause.Occur.SHOULD);

        double sim = 0;
        if (indexSearcher == null) return 1;
        try {
            TopDocs hits = indexSearcher.search(query, 1);
            if (hits.scoreDocs.length > 0)
                sim = hits.scoreDocs[0].score;
        } catch (IOException e) {
            logger.error("Error on search: {};{}", name, genre, e);
        }
        return sim;
    }

    public List<Object> search(String name, int num) {
        IntRange typeRange = new IntRange(1, 99);
        return search(name, typeRange, num);
    }

    public List<Object> search(String name, int num, boolean isSeries) {
        IntRange typeRange = isSeries ? new IntRange(1, 1) : new IntRange(2, 99);
        return search(name, typeRange, num);
    }

    public List<Object> search(String name, IntRange typeRange, int num) {
        Analyzer analyzer = new CJKAnalyzer();
        QueryParser queryParser = new QueryParser("Name", analyzer);
        Query queryName = null;
        try {
            queryName = queryParser.parse(stem(name));
        } catch (ParseException e) {
            logger.error("Error on parse: {}", name, e);
        }
        List<Object> result = new ArrayList<Object>();
        if (queryName == null) return result;

        Query queryType = NumericRangeQuery.newIntRange("TypeId", typeRange.getMinimumInteger(), typeRange.getMaximumInteger(), true, true);

        BooleanQuery query = new BooleanQuery();
        query.add(queryName, BooleanClause.Occur.MUST);
        query.add(queryType, BooleanClause.Occur.MUST);

        if (indexSearcher == null) return result;
        try {
            TopDocs hits = indexSearcher.search(query, num);
            for (ScoreDoc scoreDoc : hits.scoreDocs) {
                if (scoreDoc.score < 1) continue;
                Document doc = indexSearcher.doc(scoreDoc.doc);
                ItemRelation relation = new ItemRelation();
                relation.setRightId(Long.valueOf(doc.get("Id")));
                relation.setSimilarity((double) scoreDoc.score);
                result.add(relation);
            }
        } catch (IOException e) {
            logger.error("Error on search: {}", name, e);
        }

        return result;
    }

    private String stem(String name) {
        return name
                .replaceAll("\\[.*?\\]", "")
                .replaceAll("\\(.*?\\)", "")
                .replaceAll("^.*:", "")
                .replaceAll("[0-9a-zA-ZⅠⅡ]+$", "");
    }
}
